COMP3151/9154 Foundations of Concurrency
Term 2, 2022

Friday Code and Notes (Week 4)

Table of Contents

1 Java code

1.1 Counter

class CounterValue {
    /* 1. Volatile in C, and volatile in Java, are completely different

       In C: volatile is a way to tell the compiler that
             reads and writes to this variable
             shouldn't be optimised away.

       In Java: makes sure reads and writes to this variable are
                sequentially consistent.

       Reads/writes are *sequentially consistent* if the way they
       behave are consistent with atomic reads and writes.
       In other words: the concurrency model we've used
         has assumed that as soon as I write,
         the results become immediately observable to everybody.

       |----------------|-------------|
       | int x,y = 0;                 |
       |----------------|-------------|
       | x := 1         | y := 1      |
       | int z := y     | int a := x  |
       --------------------------------

      Q: What are the possible final values of (a,z) now?
       Can we get (1,1)?
       - Yes, if they both execute in lockstep.
       How about (0,1) or (1,0)?
       - Yes, if one process finishes before the other starts
       How about (0,0)?
       - With atomic writes, no.
       - Under *weak memory models*, yes.
         W/ low-level concurrent programming on modern multicore
          archs, you sometimes can observe results
          that don't arise from any interleaving of our reads
          and writes.
     */    
    private volatile int count;

    public CounterValue() {
        count = 0;
    }

    public void increment() {
        count++;
    }

    public int read() {
        return count;
    }
}

class CounterThread extends Thread {

    private CounterValue c;

    public CounterThread(CounterValue c) {
        this.c = c;
    }

    public void run() {
        for(int i = 0; i < 5000; i++) {
            // here, responsibility for synching access to the resource
            // belongs to the threads
            // or in other words: responsibility is diffused throughout
            // possibly the whole program

            // wait on semaphore here
            c.increment();
            // signals on a semaphore here
        }
    }
}

public class Counter {

    public static void main(String [] main) throws InterruptedException {
        CounterValue c = new CounterValue();
        Thread t1 = new CounterThread(c);
        Thread t2 = new CounterThread(c);

        t1.start();
        t2.start();

        t1.join(); // wait until t1 is finished
        t2.join();
        System.out.println("Final counter value is " + c.read());
    }

}

1.2 Counters, monitor version

class CounterValue {
    private volatile int count;

    public CounterValue() {
        count = 0;
    }

    /* A *synchronized* method
        can only have one process at a time in it.
       I.e., it's implicitly protected by a lock.
       For each object, the synchronized methods
       use the same lock.
     */
    public synchronized void increment() {
        count++;
    }

    // In this case, synching on read is pointless
    public synchronized int read() {
        return count;
    }
}

class CounterThread extends Thread {

    private CounterValue c;

    public CounterThread(CounterValue c) {
        this.c = c;
    }

    public void run() {
        for(int i = 0; i < 5000; i++) {
            c.increment();
        }
    }
}

public class Counter2 {

    public static void main(String [] main) throws InterruptedException {
        CounterValue c = new CounterValue();
        Thread t1 = new CounterThread(c);
        Thread t2 = new CounterThread(c);

        t1.start();
        t2.start();

        t1.join(); // wait until t1 is finished
        t2.join();
        System.out.println("Final counter value is " + c.read());
    }

}

1.3 Producer/consumer

/* A monitor for managing the buffer */
/* A fake buffer: we're only going to track
   whether it's full or not, and not put 
   anything in it.
 */
class Buffer {
    private int max_size;
    private volatile int current_size;

    public Buffer(int max_size) {
        this.max_size = max_size;
        this.current_size = 0;
    }

    public synchronized void enqueue() {
        /* The queue might be full.
           If so, we wait.
         */
        while(max_size == current_size) {
            // wait is "wait on a condition variable"
            // every Object in java implicitly has
            // exactly one condition variable.
            try {
                wait();
            }
            catch(InterruptedException e) {}
            // when we're here, we don't know if
            // the condition we were waiting
            // is *still* true
        }
        /* here, we really do know that max_size != current_size */
        current_size++;
        notifyAll();        
    }

    public synchronized void dequeue() {
        while(current_size == 0) {
            try {
                wait();
            }
            catch(InterruptedException e) {}
        }
        current_size--;
        notifyAll();        
    }
}

class Consumer extends Thread {
    Buffer b;

    public Consumer(Buffer b) {
        this.b = b;
    }

    public void run() {
        while(true) {
            b.enqueue();
            System.out.println("Thread #" + getId() + " enqueued a thing!");
        }
    }
}

class Producer extends Thread {
    Buffer b;

    public Producer(Buffer b) {
        this.b = b;
    }

    public void run() {
        while(true) {
            b.dequeue();
            System.out.println("Thread #" + getId() + " dequeued a thing!");
        }
    }
}

public class ProdCom {
    public static void main(String[] args) {
        Buffer b = new Buffer(1);
        Thread t1 = new Producer(b);
        Thread t2 = new Producer(b);
        Thread t3 = new Consumer(b);
        Thread t4 = new Consumer(b);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

2 Notes

signal for semaphores:
  - wake someone up, if someone is waiting
  - increment v if noone is waiting

signal for monitors:
  - wake someone up, if someone is waiting
  - do nothing if noone is waiting


E: Entering process
W: Waiting processes
S: Signalling process

 In Java:
    E = W < S
 In the Brinch Hansen cinematic universe:
    E < S < W

2022-08-05 Fri 16:47

Announcements RSS